home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / pc / ABUSESRC.ZIP / AbuseSrc / macabuse / src / innet.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-20  |  18.5 KB  |  744 lines

  1. /*
  2.  
  3.   This file is a combination of :
  4.     src/net/unix/unixnfc.c 
  5.     src/net/unix/netdrv.c 
  6.     src/net/unix/undrv.c
  7.  
  8.     netdrv & undrv compile to a stand-alone program with talk with unixnfc
  9. via a FIFO in /tmp, using a RPC-like scheme.  This versions runs inside
  10. of a abuse and therefore is a bit simpler.
  11.  
  12.  
  13. */
  14. #include "idle.hpp"
  15. #include "demo.hpp"
  16. #include "macs.hpp"
  17. #include "specs.hpp"
  18. #include "level.hpp"
  19. #include "game.hpp"
  20. #include <stdio.h>
  21. #include "timing.hpp"
  22. #include "fileman.hpp"
  23. #include "netface.hpp"
  24.  
  25. #include "gserver.hpp"
  26. #include "gclient.hpp"
  27. #include "dprint.hpp"
  28. #include "netcfg.hpp"
  29.  
  30.  
  31. extern char *symbol_str(char *name);
  32.  
  33. #ifdef __WATCOMC__
  34. #define getlogin() "DOS user"
  35. #endif
  36.  
  37.  
  38. base_memory_struct *base;   // points to shm_addr
  39. base_memory_struct local_base;
  40. net_address *net_server=NULL;
  41. extern int registered;
  42. net_protocol *prot=NULL;
  43. net_socket *comm_sock=NULL,*game_sock=NULL;
  44. extern char lsf[256];
  45. game_handler *game_face=NULL;
  46. int local_client_number=0;        // 0 is the server
  47. join_struct *join_array=NULL;      // points to an array of possible joining clients
  48. extern char *get_login();
  49. extern void set_login(char *name);
  50.  
  51. int net_init(int argc, char **argv)
  52. {
  53.   int i,x,db_level=0;
  54.   base=&local_base;
  55.  
  56.   local_client_number=0;
  57.  
  58.   if (!main_net_cfg)
  59.     main_net_cfg=new net_configuration;
  60.  
  61.  
  62.   if (!registered) return 0;
  63.  
  64.   for (i=1;i<argc;i++) 
  65.     if (!strcmp(argv[i],"-nonet"))
  66.       return 0;
  67.     else if (!strcmp(argv[i],"-port"))
  68.     {
  69.       if (i==argc-1 || !sscanf(argv[i+1],"%d",&x) || x<1 || x>0x7fff)
  70.       {
  71.         dprintf("bad value following -port, use 1..32000\n");
  72.         return 0;
  73.       } 
  74.       else 
  75.         main_net_cfg->port=x;
  76.     } else if (!strcmp(argv[i],"-net") && i<argc-1)
  77.     {
  78.       i++;
  79.       strcpy(main_net_cfg->server_name,argv[i]);
  80.       main_net_cfg->state=net_configuration::CLIENT;
  81.     }
  82.     else if (!strcmp(argv[i],"-ndb"))
  83.     {
  84.       if (i==argc-1 || !sscanf(argv[i+1],"%d",&x) || x<1 || x>3)
  85.       {
  86.         dprintf("bad value following -ndb, use 1..3\n");
  87.         return 0;
  88.       } else { db_level=x; }
  89.     } else if (!strcmp(argv[i],"-server"))
  90.       main_net_cfg->state=net_configuration::SERVER; 
  91.     else if (!strcmp(argv[i],"-min_players"))
  92.     {
  93.       i++; 
  94.       int x=atoi(argv[i]);
  95.       if (x>=1 && x<=8)
  96.         main_net_cfg->min_players=x;
  97.       else dprintf("bad value for min_players use 1..8\n");
  98.     }
  99.  
  100.   
  101.   net_protocol *n=net_protocol::first,*usable=NULL;     // find a usable protocol from installed list
  102.   int total_usable=0;
  103.   for (;n;n=n->next)                                    // show a list of usables, just to be cute
  104.   {
  105.     dprintf("Protocol %s : ",n->installed() ? "Installed" : "Not_installed");
  106.     dprintf("%s\n",n->name());
  107.     if (n->installed()) 
  108.     {
  109.       total_usable++;
  110.       usable=n;
  111.     }      
  112.   }
  113.  
  114.   if (!usable) { dprintf("No network protocols installed\n");  return 0; }
  115.   prot=usable;
  116.   prot->set_debug_printing((net_protocol::debug_type)db_level);
  117.   if (main_net_cfg->state==net_configuration::SERVER)
  118.     set_login(main_net_cfg->name);
  119.  
  120.   comm_sock=game_sock=NULL;
  121.   if (main_net_cfg->state==net_configuration::CLIENT)
  122.   {
  123.     dprintf("Attempting to locate server %s, please wait\n",main_net_cfg->server_name);
  124.     char *sn=main_net_cfg->server_name;
  125.     if (main_net_cfg->addr)
  126.       net_server = main_net_cfg->addr->copy();
  127.     else
  128.       net_server=prot->get_node_address(sn,DEFAULT_COMM_PORT,0);
  129.     if (!net_server) { dprintf(symbol_str("unable_locate"));  exit(0); }   
  130.     dprintf("Server located!  Please wait while data loads....\n");
  131.   }
  132.  
  133.   fman=new file_manager(argc,argv,prot);                                       // manages remote file access
  134.   if (game_face)
  135.     delete game_face;
  136.   game_face=new game_handler;
  137.   join_array=(join_struct *)jmalloc(sizeof(join_struct)*MAX_JOINERS,"join array");
  138.   base->join_list=NULL;
  139.   base->mem_lock=0;
  140.   base->calc_crcs=0;
  141.   base->get_lsf=0;
  142.   base->wait_reload=0;
  143.   base->need_reload=0;
  144.   base->input_state=INPUT_COLLECTING;
  145.   base->current_tick=0;
  146.   base->packet.packet_reset();
  147.  
  148.   return 1;
  149. }
  150.  
  151.  
  152.  
  153.  
  154. int net_start()  // is the game starting up off the net? (i.e. -net hostname)
  155. {   return (main_net_cfg && main_net_cfg->state==net_configuration::CLIENT);  }
  156.  
  157.  
  158.  
  159. int kill_net()
  160.   if (game_face) delete game_face;  
  161.   game_face=new game_handler;
  162.  
  163.   if (join_array) jfree(join_array);  join_array=NULL;
  164.   if (game_sock) { delete game_sock; game_sock=NULL; }
  165.   if (comm_sock) { delete comm_sock; comm_sock=NULL; }
  166.   delete fman;  fman=NULL;
  167.   if (net_server) { delete net_server; net_server=NULL; }
  168.   if (prot) 
  169.   { 
  170.   
  171.     prot->cleanup(); 
  172.     prot=NULL; 
  173.     return 1;
  174.   } else return 0;
  175. }
  176.  
  177. void net_uninit()
  178. {
  179.   kill_net();
  180. }
  181.  
  182.  
  183. int NF_set_file_server(net_address *addr)
  184. {
  185.   if (prot)
  186.   {
  187.     fman->set_default_fs(addr);
  188.     net_socket *sock=prot->connect_to_server(addr,net_socket::SOCKET_SECURE);
  189.  
  190.     if (!sock) { dprintf("set_file_server::connect failed\n"); return 0; }
  191.     uchar cmd=CLIENT_CRC_WAITER;
  192.     if ( sock->write(&cmd,1)!=1 )
  193.       dprintf("set_file_server::writefailed\n");
  194.     else
  195.       if (sock->read(&cmd,1)!=1)
  196.       {
  197.         dprintf("set_file_server::read failed\n");        // wait for confirmation that crc's are written
  198.         delete sock; 
  199.         return 0; 
  200.       }
  201.     delete sock;
  202.     return cmd; 
  203.   } else return 0;
  204. }
  205.  
  206. int NF_set_file_server(char *name)
  207. {
  208.   if (prot)
  209.   {
  210.     net_address *addr=prot->get_node_address(name,DEFAULT_COMM_PORT,0);
  211.     if (addr)
  212.     {
  213.       int ret=NF_set_file_server(addr);
  214.       delete addr;
  215.       return ret;
  216.     } else return 0;
  217.   } else return 0;
  218. }
  219.  
  220.  
  221. int NF_open_file(char *filename, char *mode)
  222. {
  223.   if (prot)
  224.     return fman->rf_open_file(filename,mode);
  225.   else return -2;
  226. }
  227.  
  228.  
  229. long NF_close(int fd)
  230. {
  231.   if (prot)
  232.     return fman->rf_close(fd);
  233.   else return 0;
  234. }
  235.  
  236. long NF_read(int fd, void *buf, long size)
  237. {
  238.   if (prot)
  239.     return fman->rf_read(fd,buf,size);
  240.   else return 0;
  241. }
  242.  
  243. long NF_filelength(int fd)
  244. {
  245.   if (prot)
  246.     return fman->rf_file_size(fd);
  247.   else return 0;
  248. }
  249.  
  250. long NF_seek(int fd, long offset)
  251. {
  252.   if (prot)
  253.     return fman->rf_seek(fd,offset);
  254.   else return 0;
  255. }
  256.  
  257. long NF_tell(int fd)
  258. {
  259.   if (prot)
  260.     return fman->rf_tell(fd);
  261.   else return 0;
  262. }
  263.  
  264.  
  265. void service_net_request() 
  266. {
  267.   if (prot)
  268.   {
  269.     if (prot->select(0))  // anything happening net-wise?
  270.     {
  271.       if (comm_sock && comm_sock->ready_to_read())  // new connection?
  272.       {
  273.         net_address *addr;
  274.             
  275.         net_socket *new_sock=comm_sock->accept(addr);    
  276.         if (new_sock)
  277.         {
  278.           uchar client_type;
  279.           if (new_sock->read(&client_type,1)!=1)
  280.           {
  281.             delete addr;    
  282.             delete new_sock;
  283.           }
  284.           else
  285.           {
  286.             switch (client_type)
  287.             {
  288.               case CLIENT_NFS :
  289.               {
  290.                 delete addr;    
  291.                 fman->add_nfs_client(new_sock);
  292.               } break;
  293.               case CLIENT_CRC_WAITER :
  294.               {        
  295.                 crc_man.write_crc_file(NET_CRC_FILENAME);       // return 0 on failure
  296.                 client_type=1;                                  // confirmation byte
  297.                 new_sock->write(&client_type,1);
  298.                 delete new_sock;                                // done with this socket now
  299.                 delete addr;    
  300.               } break;
  301.               case CLIENT_LSF_WAITER :          // wants to know which .lsp file to start with
  302.               {
  303.                 uchar len=strlen(lsf);
  304.                 new_sock->write(&len,1);
  305.                 new_sock->write(lsf,len);
  306.                 delete new_sock;
  307.                 delete addr;
  308.               } break;
  309.               default :
  310.               {
  311.                 if (!game_face || game_face->add_client(client_type,new_sock,addr)==0)  // ask server or client to add new client
  312.                 {
  313.                   delete addr;
  314.                   delete new_sock;
  315.                 }
  316.               } break;
  317.             }
  318.           }            
  319.         }
  320.       }
  321.       if (game_face && !game_face->process_net())
  322.       {
  323.         delete game_face;
  324.         game_face=new game_handler;
  325.       }
  326.       fman->process_net();      
  327.     }
  328.   }
  329. }
  330.  
  331.  
  332. int get_remote_lsf(net_address *addr, char *filename)  // filename should be 256 bytes
  333. {
  334.   if (prot)
  335.   {
  336.     net_socket *sock=prot->connect_to_server(addr,net_socket::SOCKET_SECURE);
  337.     if (!sock) return 0;
  338.  
  339.     uchar ctype=CLIENT_LSF_WAITER;
  340.     uchar len;
  341.  
  342.     if (sock->write(&ctype,1)!=1 ||
  343.         sock->read(&len,1)!=1 || len==0 ||
  344.         sock->read(filename,len)!=len)
  345.     {
  346.       delete sock; 
  347.       return 0;
  348.     } 
  349.  
  350.     delete sock;
  351.     return 1;  
  352.  
  353.   } else return 0;
  354. }
  355.  
  356. void server_check() { ; } 
  357.  
  358. int request_server_entry()
  359. {
  360.   if (prot && main_net_cfg)
  361.   {
  362.     if (!net_server) return 0;
  363.  
  364.     if (game_sock) delete game_sock;
  365.     dprintf("Joining game in progress, hang on....\n");
  366.  
  367.     int game_port = main_net_cfg->game_port;
  368.         
  369.     game_sock=prot->create_listen_socket(game_port,net_socket::SOCKET_FAST);     // this is used for fast game packet transmission
  370.     if (!game_sock) { if (comm_sock) delete comm_sock; comm_sock=NULL; prot=NULL; return 0; }
  371.     game_sock->read_selectable();
  372.     main_net_cfg->game_port = game_port;
  373.  
  374.     net_socket *sock=prot->connect_to_server(net_server,net_socket::SOCKET_SECURE);
  375.     if (!sock)
  376.     { 
  377.       dprintf("unable to connect to server\n");
  378.       return 0;
  379.     }
  380.  
  381.     uchar ctype=CLIENT_ABUSE;
  382.     ushort port=lstl(game_port),cnum;
  383.  
  384.     uchar reg;
  385.     if (sock->write(&ctype,1)!=1 ||   // send server our game port
  386.         sock->read(®,1)!=1)        // is remote engine registered?
  387.     { delete sock; return 0; }
  388.  
  389.     if (reg==2)   // too many players
  390.     {
  391.       dprintf(symbol_str("max_players"));
  392.       delete sock;
  393.       return 0;
  394.     }
  395.  
  396.     // maker sure the two games are both registered or unregistered or sync problems
  397.     // will occur.
  398.  
  399.     if (reg && !registered)
  400.     {
  401.       dprintf(symbol_str("net_not_reg"));
  402.       delete sock; 
  403.       return 0;
  404.     } 
  405.  
  406.     if (!reg && registered)
  407.     {
  408.       dprintf(symbol_str("server_not_reg"));
  409.       delete sock;
  410.       return 0;
  411.     }
  412.  
  413.     char uname[256];
  414.     if (get_login())
  415.       strcpy(uname,get_login());
  416.     else strcpy(uname,"unknown");
  417.     uchar len=strlen(uname)+1;
  418.     short nkills;
  419.  
  420.     if (sock->write(&len,1)!=1 ||
  421.         sock->write(uname,len)!=len || 
  422.         sock->write(&port,2)!=2  ||            // send server our game port
  423.         sock->read(&port,2)!=2   ||            // read server's game port
  424.         sock->read(&nkills,2)!=2 ||
  425.         sock->read(&cnum,2)!=2   || cnum==0    // read player number (cannot be 0 because 0 is server)
  426.         )
  427.     { delete sock; return 0; }
  428.  
  429.     nkills=lstl(nkills);
  430.     port=lstl(port);
  431.     cnum=lstl(cnum);
  432.     
  433.     main_net_cfg->kills=nkills;
  434.     net_address *addr=net_server->copy();
  435.     addr->set_port(port);
  436.  
  437.     if (game_face)
  438.       delete game_face;
  439.     game_face=new game_client(sock,addr);
  440.     delete addr;
  441.  
  442.     local_client_number=cnum;
  443.     return cnum;
  444.   } else return 0;
  445. }
  446.  
  447. int reload_start()
  448. {
  449.   if (prot && game_face)
  450.     return game_face->start_reload();
  451.   else return 0;
  452. }
  453.  
  454. int reload_end()
  455. {
  456.   if (prot && game_face)
  457.     return game_face->end_reload();
  458.   else return 0;
  459. }
  460.  
  461.  
  462. void net_reload()
  463. {
  464.   if (prot)
  465.   {
  466.     if (net_server)
  467.     {
  468.       if (current_level)
  469.         delete current_level;
  470.       bFILE *fp;
  471.  
  472.       if (!reload_start()) return ;
  473.  
  474.       do {            // make sure server saves the file
  475.         fp=open_file(NET_STARTFILE,"rb");
  476.         if (fp->open_failure()) { delete fp; fp=NULL; }
  477.       } while (!fp);
  478.  
  479.       spec_directory sd(fp);  
  480.  
  481.       spec_entry *e=sd.find("Copyright 1995 Crack dot Com, All Rights reserved"); 
  482.       if (!e)
  483.       { 
  484.         the_game->show_help("This level is missing copyright information, cannot load\n");
  485.         current_level=new level(100,100,"untitled");
  486.         the_game->need_refresh();
  487.       }
  488.       else 
  489.         current_level=new level(&sd,fp,NET_STARTFILE);
  490.  
  491.       delete fp;     
  492.       base->current_tick=(current_level->tick_counter()&0xff); 
  493.  
  494.       reload_end();
  495.     } else if (current_level)
  496.     {
  497.       
  498.       join_struct *join_list=base->join_list;
  499.  
  500.  
  501.       while (join_list)
  502.       {
  503.     
  504.         view *f=player_list;
  505.         for (;f && f->next;f=f->next);      // find last player, add one for pn
  506.         int i,st=0;
  507.         for (i=0;i<total_objects;i++)
  508.           if (!strcmp(object_names[i],"START"))
  509.             st=i;
  510.             
  511.         game_object *o=create(current_start_type,0,0);
  512.         game_object *start=current_level->get_random_start(320,NULL);
  513.         if (start) { o->x=start->x; o->y=start->y; }
  514.         else { o->x=100; o->y=100; }
  515.             
  516.         f->next=new view(o,NULL,join_list->client_id);
  517.         strcpy(f->next->name,join_list->name);
  518.         o->set_controller(f->next);
  519.             
  520.         if (start)
  521.           current_level->add_object_after(o,start);
  522.         else
  523.           current_level->add_object(o);
  524.             
  525.         view *v=f->next;      
  526.             
  527.         join_list=join_list->next;
  528.       }     
  529.       base->join_list=NULL;
  530.       current_level->save(NET_STARTFILE,1);
  531.       base->mem_lock=0;
  532.  
  533.  
  534.       jwindow *j=eh->new_window(0,yres/2,-1,-1,new info_field(WINDOW_FRAME_LEFT,
  535.           WINDOW_FRAME_TOP,
  536.           0,symbol_str("resync"),
  537.           new button(WINDOW_FRAME_LEFT,
  538.               WINDOW_FRAME_TOP+eh->font()->height()+5,ID_NET_DISCONNECT,
  539.               symbol_str("slack"),NULL)),symbol_str("hold!"))
  540.         ;
  541.  
  542.   
  543.  
  544.       eh->flush_screen();
  545.       if (!reload_start()) return ;
  546.  
  547.       base->input_state=INPUT_RELOAD;    // if someone missed the game tick with the RELOAD data in it, make sure the get it
  548.   
  549.       // wait for all client to reload the level with the new players
  550.       do  
  551.       { 
  552.         service_net_request();
  553.         if (eh->event_waiting())
  554.         {
  555.           event ev;
  556.           do
  557.           {           
  558.             eh->get_event(ev);
  559.             if (ev.type==EV_MESSAGE && ev.message.id==ID_NET_DISCONNECT)
  560.             {
  561.               if (game_face)
  562.                 game_face->end_reload(1);
  563.               base->input_state=INPUT_PROCESSING; 
  564.             }
  565.             
  566.           } while (eh->event_waiting()); 
  567.             
  568.           eh->flush_screen();
  569.         } else 
  570.         {
  571.           if (idle_man)
  572.             idle_man->idle();
  573.         }
  574.  
  575.       } while (!reload_end());  
  576.       eh->close_window(j);
  577.       unlink(NET_STARTFILE);
  578.  
  579.       the_game->reset_keymap();
  580.  
  581.       base->input_state=INPUT_COLLECTING;
  582.  
  583.     }     
  584.   }
  585. }
  586.  
  587.  
  588. int client_number() { return local_client_number; }
  589.  
  590.  
  591. void send_local_request()
  592. {
  593.  
  594.   if (prot)
  595.   {    
  596.     if (current_level)
  597.       base->current_tick=(current_level->tick_counter()&0xff); 
  598.     if (game_face)
  599.       game_face->add_engine_input();
  600.   } else base->input_state=INPUT_PROCESSING;
  601.  
  602. }
  603.  
  604.  
  605. void kill_slackers()
  606. {
  607.   if (prot)
  608.   {
  609.     if (game_face && !game_face->kill_slackers())
  610.     {
  611.       delete game_face;
  612.       game_face=new game_handler();
  613.     }
  614.   }
  615. }
  616.  
  617. int get_inputs_from_server(unsigned char *buf)
  618. {
  619.   if (idle_man)
  620.     idle_man->idle_reset();
  621.  
  622.   if (prot && base->input_state!=INPUT_PROCESSING)      // if input is not here, wait on it
  623.   {
  624.     time_marker start;
  625.  
  626.     int total_retry=0;
  627.     jwindow *abort=NULL;
  628.  
  629.     while (base->input_state!=INPUT_PROCESSING)
  630.     { 
  631.       if (!prot)
  632.       { 
  633.         base->input_state=INPUT_PROCESSING; 
  634.         return 1; 
  635.       }
  636.       service_net_request();
  637.  
  638.       time_marker now;                   // if this is taking to long, the packet was probably lost, ask for it to be resent
  639.  
  640.       if (now.diff_time(&start)>0.05)
  641.       {
  642.         if (idle_man)
  643.           idle_man->idle();
  644.         if (prot->debug_level(net_protocol::DB_IMPORTANT_EVENT))
  645.           dprintf("(missed packet)");
  646.             
  647.         if (game_face)
  648.           game_face->input_missing();
  649.         start.get_time();
  650.             
  651.         total_retry++;
  652.         if (total_retry==12000)    // 2 minutes and nothing
  653.         {
  654.           abort=eh->new_window(0,yres/2,-1,eh->font()->height()*4,
  655.               new info_field(WINDOW_FRAME_LEFT,
  656.                   WINDOW_FRAME_TOP,
  657.                   0,symbol_str("waiting"),
  658.                   new button(WINDOW_FRAME_LEFT,
  659.                       WINDOW_FRAME_TOP+eh->font()->height()+5,ID_NET_DISCONNECT,
  660.                       symbol_str("slack"),NULL)),symbol_str("Error"));      
  661.           eh->flush_screen();
  662.         }
  663.       }
  664.       if (abort)
  665.       {
  666.         if (eh->event_waiting())
  667.         {
  668.           event ev;
  669.           do
  670.           {
  671.             eh->get_event(ev);
  672.             if (ev.type==EV_MESSAGE && ev.message.id==ID_NET_DISCONNECT)
  673.             {
  674.               kill_slackers();
  675.               base->input_state=INPUT_PROCESSING; 
  676.             }
  677.           } while (eh->event_waiting());
  678.             
  679.           eh->flush_screen();
  680.         }
  681.       }
  682.     }
  683.  
  684.     if (abort)
  685.     {
  686.       eh->close_window(abort);
  687.       the_game->reset_keymap();
  688.  
  689.     }
  690.   }
  691.  
  692.  
  693.   memcpy(base->last_packet.data,base->packet.data,base->packet.packet_size()+base->packet.packet_prefix_size());
  694.   
  695.   int size=base->packet.packet_size();  
  696.   memcpy(buf,base->packet.packet_data(),size);
  697.  
  698.   base->packet.packet_reset();
  699.   base->mem_lock=0;
  700.  
  701.   return size;
  702. }
  703.  
  704. int become_server(char *name)
  705. {
  706.   if (prot && main_net_cfg)
  707.   {
  708.  
  709.     if (comm_sock) delete comm_sock;
  710.     
  711.     int port = main_net_cfg->port, gameport = main_net_cfg->game_port;
  712.     
  713.     comm_sock=prot->create_listen_socket(port,net_socket::SOCKET_SECURE);   // this is used for incomming connections
  714.     main_net_cfg->port = port;
  715.  
  716.     if (!comm_sock) { prot=NULL; return 0; }
  717.     comm_sock->read_selectable();
  718.     prot->start_notify(0x9090, name, strlen(name));  // should we define a new socket for notifiers?
  719.  
  720.     if (game_sock) delete game_sock;
  721.     game_sock=prot->create_listen_socket(gameport,net_socket::SOCKET_FAST);     // this is used for fast game packet transmission
  722.     if (!game_sock) { if (comm_sock) delete comm_sock; comm_sock=NULL; prot=NULL; return 0; }
  723.     game_sock->read_selectable();
  724.     main_net_cfg->game_port = gameport;
  725.  
  726.     if (game_face)
  727.       delete game_face;
  728.     game_face=new game_server;
  729.     local_client_number=0;
  730.     return 1;
  731.   }
  732.   return 0;
  733. }
  734.  
  735. void read_new_views() { ; }
  736.  
  737.  
  738. void wait_min_players()
  739. {
  740.   if (game_face) 
  741.     game_face->game_start_wait();
  742. }
  743.